﻿using codeparser.net.v2.Entities;
using codeparser.net.v2.Exceptions;
using codeparser.net.v2.Lexer;
using codeparser.net.v2.Validators;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace codeparser.net.v2.Tests
{
    /// <summary>
    /// Contains the tests for the parser.
    /// </summary>
    [TestClass]
    public class ParserTests
    {
        /// <summary>
        /// Contains the parser.
        /// </summary>
        private Parser _parser;

        /// <summary>
        /// Initializes a test.
        /// </summary>
        [TestInitialize()]
        public void InitializeTest()
        {
            // Create a tag configuration.
            TagConfiguration tagConfiguration = new TagConfiguration();
            tagConfiguration.Add(new Tag(
                new TagElement("b"), TagClosingStyle.Required));
            tagConfiguration.Add(new Tag(
                new TagElement("i"), TagClosingStyle.Required));
            tagConfiguration.Add(new Tag(
                new TagElement("flagEU"), TagClosingStyle.None));
            tagConfiguration.Add(new Tag(
                new TagElement("url"), TagClosingStyle.Required,
                new TagAttribute("href", AttributeStyles.Optional | AttributeStyles.Default, AttributeValueStyles.Required),
                new TagAttribute("target", AttributeStyles.Optional, AttributeValueStyles.Required),
                new TagAttribute("enabled", AttributeStyles.Optional, AttributeValueStyles.None)));

            // Create a lexer.
            this._parser = new Parser(tagConfiguration);
        }

        /// <summary>
        /// Tests null input.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void TestNull()
        {
            string input = null;

            string actual = this._parser.Parse(input);
        }

        /// <summary>
        /// Tests empty input.
        /// </summary>
        [TestMethod]
        public void TestEmpty()
        {
            string input = "";

            string actual = this._parser.Parse(input);

            Assert.AreEqual("", actual);
        }

        /// <summary>
        /// Tests normal text.
        /// </summary>
        [TestMethod]
        public void TestText()
        {
            string input = "Hallo Welt!";

            string actual = this._parser.Parse(input);

            Assert.AreEqual("Hallo Welt!", actual);
        }

        /// <summary>
        /// Tests a missing rule.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(MissingRuleException))]
        public void TestMissingRule()
        {
            string input = "Hallo [b]Welt[/b]!";

            string actual = this._parser.Parse(input);
        }

        /// <summary>
        /// Tests text within tags.
        /// </summary>
        [TestMethod]
        public void TestTextWithinTags()
        {
            this._parser.TagConfiguration["b"].Rules.Add(new TagRule("<b>", "</b>"));

            string input = "Hallo [b]Welt[/b]!";

            string actual = this._parser.Parse(input);

            Assert.AreEqual("Hallo <b>Welt</b>!", actual);
        }

        /// <summary>
        /// Tests to add a rule to a non-existing tag.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(NullReferenceException))]
        public void TestAddRuleForUnknownTag()
        {
            this._parser.TagConfiguration["foo"].Rules.Add(new TagRule("<foo>", "</foo>"));
        }

        /// <summary>
        /// Tests nested tags.
        /// </summary>
        [TestMethod]
        public void TestNestedTags()
        {
            this._parser.TagConfiguration["b"].Rules.Add(new TagRule("<b>", "</b>"));
            this._parser.TagConfiguration["i"].Rules.Add(new TagRule("<i>", "</i>"));

            string input = "[i]Hallo [b]Welt[/b][/i]!";

            string actual = this._parser.Parse(input);

            Assert.AreEqual("<i>Hallo <b>Welt</b></i>!", actual);
        }

        /// <summary>
        /// Tests invalid nested tags.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(CodeparserException))]
        public void TestInvalidNestedTags()
        {
            this._parser.TagConfiguration["b"].Rules.Add(new TagRule("<b>", "</b>"));
            this._parser.TagConfiguration["i"].Rules.Add(new TagRule("<i>", "</i>"));

            string input = "[i]Hallo [b]Welt[/i][/b]!";

            string actual = this._parser.Parse(input);
        }

        /// <summary>
        /// Tests a content attribute.
        /// </summary>
        [TestMethod]
        public void TestContentAttribute()
        {
            this._parser.TagConfiguration["url"].Rules.Add(new TagRule("<a href=\"${content}\">", "</a>"));

            string input = "[url]http://www.goloroden.de[/url]";

            string actual = this._parser.Parse(input);

            Assert.AreEqual("<a href=\"http://www.goloroden.de\">http://www.goloroden.de</a>", actual);
        }

        /// <summary>
        /// Tests the default attribute.
        /// </summary>
        [TestMethod]
        public void TestDefaultAttribute()
        {
            this._parser.TagConfiguration["url"].Rules.Add(new TagRule("<a href=\"${href}\">", "</a>"));

            string input = "[url=http://www.goloroden.de]goloroden.de[/url]";

            string actual = this._parser.Parse(input);

            Assert.AreEqual("<a href=\"http://www.goloroden.de\">goloroden.de</a>", actual);
        }
        
        /// <summary>
        /// Tests an attribute.
        /// </summary>
        [TestMethod]
        public void TestAttribute()
        {
            this._parser.TagConfiguration["url"].Rules.Add(new TagRule("<a href=\"${href}\">", "</a>"));

            string input = "[url href=http://www.goloroden.de]goloroden.de[/url]";

            string actual = this._parser.Parse(input);

            Assert.AreEqual("<a href=\"http://www.goloroden.de\">goloroden.de</a>", actual);
        }
    }
}